﻿using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using System.Net.Sockets;
using System.Text;

namespace SGraphicer
{
    public partial class SGraphicer : Form
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        /// <summary>
        /// Connection to the graphics-adapter.
        /// </summary>
        protected Microsoft.DirectX.Direct3D.Device GraphicsDevice;

        private Surface CombinedStereoImage;
        private Surface LeftBackgroundImage;
        private Surface RightBackgroundImage;
        private Surface LeftOffScreenRenderSurface;
        private Surface RightOffScreenRenderSurface;
        private Surface LeftTemporaryOffScreenSurface;
        private Surface RightTemporaryOffScreenSurface;

        /// <summary>
        /// Rectangle for storing the size of the current window, used as input on StretchRectangle-calls.
        /// </summary>
        public Rectangle FullScreen;

        private int WindowWidth = 800;
        private int WindowHeight = 600;
        private bool IsFullScreen = false;

        public bool SwapEyes = false;
        private bool DrawBackground = true;

        public SGraphicer(int WindowWidth, int WindowHeight, bool IsFullScreen, Bitmap LeftImage, Bitmap RightImage)
        {
            this.WindowWidth = WindowWidth;
            this.WindowHeight = WindowHeight;
            this.IsFullScreen = IsFullScreen;

            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            InitializeGraphics(LeftImage, RightImage);

            LoadContent();
        }

        protected virtual void LoadContent() { }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.Size = new System.Drawing.Size(WindowWidth, WindowHeight);
            this.Text = "Nvidia 3D Vision test";
        }

        public void InitializeGraphics(Bitmap LeftImage, Bitmap RightImage)
        {
            PresentParameters presentParams = new PresentParameters();

            presentParams.Windowed = !IsFullScreen;

            presentParams.BackBufferFormat = Format.A8R8G8B8;
            presentParams.BackBufferWidth = WindowWidth;
            presentParams.BackBufferHeight = WindowHeight;
            presentParams.BackBufferCount = 1;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.PresentationInterval = PresentInterval.One;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.PresentFlag = presentParams.PresentFlag | PresentFlag.LockableBackBuffer;
            GraphicsDevice = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

            CombinedStereoImage = GraphicsDevice.CreateOffscreenPlainSurface(WindowWidth * 2, WindowHeight + 1, Format.A8R8G8B8, Pool.Default);

            LeftTemporaryOffScreenSurface = GraphicsDevice.CreateOffscreenPlainSurface(WindowWidth, WindowHeight, Format.A8R8G8B8, Pool.SystemMemory);
            RightTemporaryOffScreenSurface = GraphicsDevice.CreateOffscreenPlainSurface(WindowWidth, WindowHeight, Format.A8R8G8B8, Pool.SystemMemory);

            if (LeftImage != null && RightImage != null)
            {
                LeftBackgroundImage = Microsoft.DirectX.Direct3D.Surface.FromBitmap(GraphicsDevice, LeftImage, Pool.Default);
                RightBackgroundImage = Surface.FromBitmap(GraphicsDevice, RightImage, Pool.Default);
            }
            else
            {
                DrawBackground = false;
            }

            LeftOffScreenRenderSurface = GraphicsDevice.CreateRenderTarget(WindowWidth, WindowHeight, Format.A8R8G8B8, MultiSampleType.None, 0, true);
            RightOffScreenRenderSurface = GraphicsDevice.CreateRenderTarget(WindowWidth, WindowHeight, Format.A8R8G8B8, MultiSampleType.None, 0, true);

            FullScreen = new Rectangle(0, 0, WindowWidth, WindowHeight);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            Surface oldTarget = GraphicsDevice.GetRenderTarget(0);

            {
                GraphicsDevice.SetRenderTarget(0, LeftOffScreenRenderSurface);

                GraphicsDevice.BeginScene();

                GraphicsDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Gray, 1.0f, 0);

                if (DrawBackground)
                    GraphicsDevice.StretchRectangle(RightBackgroundImage, FullScreen, LeftOffScreenRenderSurface, FullScreen, TextureFilter.None);

                RenderLeft();

                GraphicsDevice.EndScene();

                GraphicsDevice.GetRenderTargetData(LeftOffScreenRenderSurface, LeftTemporaryOffScreenSurface);
            }

            {
                GraphicsDevice.SetRenderTarget(0, RightOffScreenRenderSurface);

                GraphicsDevice.BeginScene();

                GraphicsDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Gray, 1.0f, 0);

                if(DrawBackground)
                    GraphicsDevice.StretchRectangle(LeftBackgroundImage, FullScreen, RightOffScreenRenderSurface, FullScreen, TextureFilter.None);

                RenderRight();

                GraphicsDevice.EndScene();

                GraphicsDevice.GetRenderTargetData(RightOffScreenRenderSurface, RightTemporaryOffScreenSurface);
            }

            GraphicsDevice.SetRenderTarget(0, oldTarget);

            DrawScreen();

            this.Invalidate();
        }

        protected virtual void RenderLeft() { }

        protected virtual void RenderRight() { }

        private void Set3D()
        {
            if (SwapEyes)
            {
                GraphicsDevice.UpdateSurface(LeftTemporaryOffScreenSurface, CombinedStereoImage, new Point(0, 0));
                GraphicsDevice.UpdateSurface(RightTemporaryOffScreenSurface, CombinedStereoImage, new Point(WindowWidth, 0));
            }
            else
            {
                GraphicsDevice.UpdateSurface(RightTemporaryOffScreenSurface, CombinedStereoImage, new Point(0, 0));
                GraphicsDevice.UpdateSurface(LeftTemporaryOffScreenSurface, CombinedStereoImage, new Point(WindowWidth, 0));
            }

            GraphicsStream gStream = CombinedStereoImage.LockRectangle(LockFlags.None);

            byte[] data = new byte[] {0x4e, 0x56, 0x33, 0x44,   //NVSTEREO_IMAGE_SIGNATURE         = 0x4433564e;
                                      0x00, 0x0F, 0x00, 0x00,   //Screen width * 2 = 1920*2 = 3840 = 0x00000F00;
                                      0x38, 0x04, 0x00, 0x00,   //Screen height = 1080             = 0x00000438;
                                      0x20, 0x00, 0x00, 0x00,   //dwBPP = 32                       = 0x00000020;
                                      0x02, 0x00, 0x00, 0x00};  //dwFlags = SIH_SCALE_TO_FIT       = 0x00000002;



            gStream.Seek(WindowWidth * 2 * WindowHeight * 4, System.IO.SeekOrigin.Begin);   //last row
            gStream.Write(data, 0, data.Length);

            gStream.Close();

            CombinedStereoImage.UnlockRectangle();
        }

        private void DrawScreen()
        {
            GraphicsDevice.BeginScene();

            GraphicsDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Yellow, 1.0f, 0);

            Set3D();

            Surface BackBuffer = GraphicsDevice.GetBackBuffer(0, 0, BackBufferType.Mono);
            GraphicsDevice.StretchRectangle(CombinedStereoImage, new Rectangle(0, 0, WindowWidth * 2, WindowHeight + 1), BackBuffer, new Rectangle(0, 0, WindowWidth, WindowHeight), TextureFilter.None);
            BackBuffer.ReleaseGraphics();

            GraphicsDevice.EndScene();

            GraphicsDevice.Present();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }

            base.Dispose(disposing);
        }
    }
}
